home *** CD-ROM | disk | FTP | other *** search
- /*************************************************************
- Copyright (c) 1993,1994 by Paul Long All rights reserved.
- **************************************************************/
-
- /*************************************************************
- rules.c - This source file contains a typical set of
- rules in the rules() function that define
- a simple metrics tool.
- **************************************************************/
-
-
- #include <limits.h>
- #include "metre.h"
-
- /*
- In addition to the parser's command-line options, e.g., -D, the rules look
- for a -Q option to suppress its verbose key and explanation that it
- otherwise generates at the beginning of the output.
- */
- #define QUIET_OPT_CHAR 'Q'
-
- /* Defines for warnings that are generated by the rules, not the parser. */
- #define W_GOTO 0, "goto statement used (%u time%s)"
- #define W_CONTINUE 1, "continue statement used (%u time%s)"
- #define W_RETURNS 2, "Multiple return statements (%u)"
- #define W_TAB_SPACE_INDENT 3, "Tab & space used on same line for indention"
- #define W_MULT_STATEMENTS 4, "Multiple statements (%u) per line"
- #define W_NONSTANDARD 5, "Non-standard character (0x%x) encountered"
-
- /* Nicety to evaluate as "s" if argument is greater than one; otherwise, "". */
- #define NUMBER(x) ((x) > 1 ? "s" : "")
-
- /* Assign max value to integer argument regardless of type, e.g., long int. */
- #define TYPE_MAX(x) ((x = ~0) < 0 ? x = ~(1 << sizeof x * CHAR_BIT - 1) : x)
-
- /*
- The rules maintain their function statistics in this structure.
- Could have also been just a bunch of separate variables, though.
- */
- static struct {
- struct {
- unsigned min, max;
- } decisions; /* Min and max binary decision points in function. */
- struct {
- unsigned min, max; /* Min and max lines in a function. */
- } lines;
- } per_func;
-
- static BOOLEAN mixed_indent_lines; /* Whether spaces _&_ tabs in indent. */
- static unsigned mod_statements; /* Total statements in module (file). */
- static unsigned mod_func_lines; /* Total function lines in module. */
- static unsigned fcn_gotos; /* How many gotos in a function. */
- static unsigned fcn_continues; /* How many continues in a function. */
- static unsigned fcn_returns; /* How many returns in a function. */
- static unsigned fcn_depth_max; /* Maximum nesting level in a function.*/
-
- void rules(void)
- {
- /*
- If at the beginning of a project and the quiet option has not been
- specified, print the key and explanation.
- */
- if (prj.begin && !option(QUIET_OPT_CHAR))
- {
- fprintf(out_fp, "Explanation and Guidelines:\n");
- fprintf(out_fp, "< Minimum\n");
- fprintf(out_fp, "> Maximum\n");
- fprintf(out_fp, "~ Average\n");
- fprintf(out_fp, "(nothing) Total\n");
- fprintf(out_fp, "CCI McCabe's Cyclomatic Complexity Index. Number of binary\n");
- fprintf(out_fp, " decision points plus one. Should be less than 10.\n");
- fprintf(out_fp, "Depth Maximum control structure depth. Should be less than 6.\n\n");
- fprintf(out_fp, "A goto probably indicates that there should be another function. Elses\n");
- fprintf(out_fp, "are probably a better solution than multiple return statements. An else\n");
- fprintf(out_fp, "is probably a better solution than a continue statement. A good size\n");
- fprintf(out_fp, "for a module is 30 lines or so with an upper limit of 60 lines. One\n");
- fprintf(out_fp, "statement per line. Don't mix tabs and spaces for indention.\n\n");
- }
-
- /* At the beginning of the module, initialize variables. */
- if (mod.begin)
- {
- per_func.decisions.max = 0;
- TYPE_MAX(per_func.decisions.min);
- per_func.lines.max = 0;
- TYPE_MAX(per_func.lines.min);
- mod_func_lines = 0;
- mod_statements = 0;
- mixed_indent_lines = FALSE;
- }
-
- /* At the end of the module, print a module summary. */
- if (mod.end)
- {
- fprintf(out_fp, "\n\nModule Summary\n");
- if (mod.functions != 0)
- {
- fprintf(out_fp, " CCI: <%u >%u ~%u\n",
- per_func.decisions.min + 1, per_func.decisions.max + 1,
- mod.decisions / mod.functions + 1);
- fprintf(out_fp, " Lines: <%u >%u ~%u %u\n",
- per_func.lines.min, per_func.lines.max,
- mod_func_lines / mod.functions, mod.lines.total);
- }
- fprintf(out_fp, " Exec: %u\n", mod.lines.exec);
- fprintf(out_fp, " Comment: %u\n", mod.lines.com);
- fprintf(out_fp, " White: %u\n", mod.lines.white);
- fprintf(out_fp, " Statements: %u\n", mod_statements);
- fprintf(out_fp, " Functions: %u\n", mod.functions);
-
- if (mixed_indent_lines)
- warn(W_TAB_SPACE_INDENT);
- }
-
- /* At the beginning of each function, initialize variables. */
- if (fcn.begin)
- {
- fcn_gotos = 0;
- fcn_continues = 0;
- fcn_returns = 0;
- fcn_depth_max = 0;
- }
-
- /*
- At the end of each function, set module-wide statistics variables
- and print a function summary.
- */
- if (fcn.end)
- {
- if (fcn.decisions > per_func.decisions.max)
- per_func.decisions.max = fcn.decisions;
- if (fcn.decisions < per_func.decisions.min)
- per_func.decisions.min = fcn.decisions;
-
- if (fcn.lines.total > per_func.lines.max)
- per_func.lines.max = fcn.lines.total;
- if (fcn.lines.total < per_func.lines.min)
- per_func.lines.min = fcn.lines.total;
-
- mod_func_lines += fcn.lines.total;
- mod_statements += fcn.low + fcn.high;
-
- fprintf(out_fp, "\n%s()\n", fcn_name());
- fprintf(out_fp, " CCI: %u\n", fcn.decisions + 1);
- fprintf(out_fp, " Lines: %u\n", fcn.lines.total);
- fprintf(out_fp, " Exec: %u\n", fcn.lines.exec);
- fprintf(out_fp, " Comment: %u\n", fcn.lines.com);
- fprintf(out_fp, " White: %u\n", fcn.lines.white);
- fprintf(out_fp, " Statements: %u\n", fcn.low + fcn.high);
- fprintf(out_fp, " Depth: >%u\n", fcn_depth_max);
- if (fcn_gotos > 0)
- warn(W_GOTO, fcn_gotos, NUMBER(fcn_gotos));
- if (fcn_continues > 0)
- warn(W_CONTINUE, fcn_continues, NUMBER(fcn_continues));
- if (fcn_returns > 1)
- warn(W_RETURNS, fcn_returns);
- }
-
- /* At the end of each statement, determine if greatest depth yet reached. */
- if (stm.end)
- if (stm.depth > fcn_depth_max)
- fcn_depth_max = stm.depth;
-
- /* Remember if line found with mixed indention (spaces and tabs). */
- if (lin.is_mixed_indent)
- mixed_indent_lines = TRUE;
-
- /* Print warning if a line contains more than one statement. */
- if (lin.statements > 0)
- warn(W_MULT_STATEMENTS, lin.statements + 1);
-
- /* Count gotos, continues, and returns. */
- if (keyword("goto"))
- ++fcn_gotos;
-
- if (keyword("continue"))
- ++fcn_continues;
-
- if (keyword("return"))
- ++fcn_returns;
-
- /*
- If character encountered that is not in Standard C's source
- character set, print warning.
- */
- if (lex.nonstandard)
- warn(W_NONSTANDARD, lex.nonstandard);
- }
-